package com.web3.management.service;

import com.web3.management.dto.tm.QrCapture;
import com.web3.management.dto.tm.SessionCapture;
import com.web3.management.entity.Account;
import com.web3.management.entity.AccountCredentialLog;
import com.web3.management.exception.BusinessException;
import com.web3.management.repository.AccountCredentialLogRepository;
import com.web3.management.repository.AccountRepository;
import com.web3.management.util.DataEncryptor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.Instant;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 账号凭证处理服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AccountCredentialService {

    public static final String CAPTURE_TYPE_SESSION = "SESSION";
    public static final String CAPTURE_TYPE_QR = "QR";

    private final AccountCredentialLogRepository credentialLogRepository;
    private final AccountRepository accountRepository;
    private final DataEncryptor dataEncryptor;
    private final OperationLogService operationLogService;
    @Transactional
    public void handleSessionCapture(Integer accountId, String clientId, String payloadRaw, SessionCapture capture,
                                     String status, String message) {
        Account account = accountRepository.findById(accountId)
                .orElseThrow(() -> new BusinessException("账号不存在"));

        AccountCredentialLog logEntry = buildLog(accountId, clientId, CAPTURE_TYPE_SESSION, payloadRaw, capture == null ? null : capture.getCapturedAt(), status, message);
        credentialLogRepository.save(logEntry);

        if (!"SUCCESS".equalsIgnoreCase(status) || capture == null) {
            log.warn("账号 {} 会话上报失败或为空: status={}, message={}", accountId, status, message);
            return;
        }

        if (StringUtils.hasText(capture.getCookies())) {
            account.setCookies(capture.getCookies());
        }

        String csrfToken = capture.getCsrfToken();
        if (StringUtils.hasText(csrfToken)) {
            account.setCsrfToken(csrfToken);
        }

        accountRepository.save(account);
        operationLogService.record("SYNC_ACCOUNT_SESSION", "ACCOUNT", accountId,
                String.format("客户端 %s 同步登录信息", clientId));
    }

    @Transactional
    public void handleQrCapture(Integer accountId, String clientId, String payloadRaw, QrCapture capture,
                                String status, String message) {
        AccountCredentialLog logEntry = buildLog(accountId, clientId, CAPTURE_TYPE_QR, payloadRaw,
                capture == null ? null : capture.getCapturedAt(), status, message);
        credentialLogRepository.save(logEntry);
    }

    public Optional<AccountCredentialLog> findLatest(Integer accountId, String captureType) {
        return credentialLogRepository.findFirstByAccountIdAndCaptureTypeOrderByCapturedAtDesc(accountId, captureType);
    }

    public List<AccountCredentialLog> findRecent(Integer accountId) {
        return credentialLogRepository.findTop10ByAccountIdOrderByCapturedAtDesc(accountId);
    }

    public Map<Integer, AccountCredentialLog> findLatestByAccountIds(List<Integer> accountIds, String captureType) {
        if (CollectionUtils.isEmpty(accountIds)) {
            return Collections.emptyMap();
        }
        return credentialLogRepository.findByAccountIdIn(accountIds).stream()
                .filter(log -> captureType == null || captureType.equalsIgnoreCase(log.getCaptureType()))
                .collect(Collectors.toMap(AccountCredentialLog::getAccountId, Function.identity(),
                        (existing, candidate) -> candidate.getCapturedAt() != null
                                && (existing.getCapturedAt() == null || candidate.getCapturedAt().after(existing.getCapturedAt()))
                                ? candidate : existing));
    }

    private AccountCredentialLog buildLog(Integer accountId, String clientId, String captureType, String payloadRaw,
                                          Long capturedAt, String status, String message) {
        AccountCredentialLog logEntry = new AccountCredentialLog();
        logEntry.setAccountId(accountId);
        logEntry.setClientId(clientId);
        logEntry.setCaptureType(captureType);
        logEntry.setStatus(status);
        logEntry.setMessage(message);
        logEntry.setPayload(payloadRaw);
        if (capturedAt != null) {
            logEntry.setCapturedAt(Date.from(Instant.ofEpochMilli(capturedAt)));
        } else {
            logEntry.setCapturedAt(new Date());
        }
        return logEntry;
    }

    private String extractCsrfToken(SessionCapture capture) {
        if (capture == null) {
            return null;
        }
        if (StringUtils.hasText(capture.getCookies())) {
            String token = parseFromCookies(capture.getCookies());
            if (StringUtils.hasText(token)) {
                return token;
            }
        }
        if (!CollectionUtils.isEmpty(capture.getHeadersByUrl())) {
            for (SessionCapture.HeaderCapture headerCapture : capture.getHeadersByUrl().values()) {
                if (headerCapture == null || CollectionUtils.isEmpty(headerCapture.getHeaders())) {
                    continue;
                }
                for (Map.Entry<String, String> entry : headerCapture.getHeaders().entrySet()) {
                    String key = entry.getKey();
                    String value = entry.getValue();
                    if (!StringUtils.hasText(key) || !StringUtils.hasText(value)) {
                        continue;
                    }
                    String lowerKey = key.toLowerCase(Locale.ROOT);
                    if (lowerKey.contains("csrf")) {
                        return value;
                    }
                }
            }
        }
        if (!CollectionUtils.isEmpty(capture.getStorageItems())) {
            for (SessionCapture.StorageItem item : capture.getStorageItems()) {
                if (item == null || !StringUtils.hasText(item.getKey()) || !StringUtils.hasText(item.getValue())) {
                    continue;
                }
                String lowerKey = item.getKey().toLowerCase(Locale.ROOT);
                if (lowerKey.contains("csrf")) {
                    return item.getValue();
                }
            }
        }
        return null;
    }

    private String parseFromCookies(String cookies) {
        if (!StringUtils.hasText(cookies)) {
            return null;
        }
        String[] parts = cookies.split(";");
        for (String part : parts) {
            String trimmed = part.trim();
            if (!StringUtils.hasText(trimmed)) {
                continue;
            }
            int idx = trimmed.indexOf('=');
            if (idx <= 0) {
                continue;
            }
            String name = trimmed.substring(0, idx).trim().toLowerCase(Locale.ROOT);
            String value = trimmed.substring(idx + 1).trim();
            if (name.contains("csrf")) {
                return value;
            }
        }
        return null;
    }
}
